/**
 * 
 */
package junweixu.jreader;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.text.DecimalFormat;
import java.util.Vector;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;

/**
 * @author HiyJeain
 * 
 */
public class TXTPageFactory
{

    private boolean isFirst = false;
    private boolean isLast = false;
    private String mCharType = "GBK";
    private int mLineCount;
    private float mVisibleHeight;
    private float mVisibleWidth;

    private File mBookFile = null;
    private String mBookPath = "";
    private MappedByteBuffer mBuf = null;

    private int mBufLen = 0;
    private int mBufBegin = 0;
    private int mBufEnd = 0;

    private Bitmap mBookBg = null;
    private int mScreenWidth;
    private int mScreenHeight;
    private int mFontSize = 24;
    private int mTextColor = 0xFF2E2EB2;
    private int mBGColor = 0xffff9e85; // 背景颜色
    private int mMarginWidth = 15; // 左右与边缘的距离
    private int mMarginHeight = 20; // 上下与边缘的距离

    private Vector<String> mLines = new Vector<String>();

    private Paint mPaint;

    public TXTPageFactory(int mScreenWidth, int mScreenHeight,
            String mBookPath, int mBufEnd)
    {
        this.mScreenHeight = mScreenHeight;
        this.mScreenWidth = mScreenWidth;
        this.mBookPath = mBookPath;
        this.mBufEnd = mBufEnd;

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setTextAlign(Align.LEFT);
        mPaint.setTextSize(mFontSize);
        mPaint.setColor(mTextColor);

        mVisibleWidth = mScreenWidth - mMarginWidth * 2;
        mVisibleHeight = mScreenHeight - mMarginHeight * 2;
        mLineCount = (int) (mVisibleHeight / mFontSize);
    }

    public void openBook() throws IOException
    {
        mBookFile = new File(mBookPath);
        mCharType = get_charset(mBookFile);
        long lLen = mBookFile.length();
        mBufLen = (int) lLen;
        mBuf = new RandomAccessFile(mBookFile, "r").getChannel().map(
                FileChannel.MapMode.READ_ONLY, 0, lLen);

    }

    protected byte[] readPreviousParagraph(int mFromPos)
    {
        int mEnd = mFromPos;
        int i;
        byte b0, b1;
        if (mCharType.equals("UTF-16LE"))
        {
            i = mEnd - 2;
            while (i > 0)
            {
                b0 = mBuf.get(i);
                b1 = mBuf.get(i + 1);
                if (b0 == 0x0a && b1 == 0x00 && i != mEnd - 2)
                {
                    i += 2;
                    break;
                }
                i--;
            }

        } else if (mCharType.equals("UTF-16BE"))
        {
            i = mEnd - 2;
            while (i > 0)
            {
                b0 = mBuf.get(i);
                b1 = mBuf.get(i + 1);
                if (b0 == 0x00 && b1 == 0x0a && i != mEnd - 2)
                {
                    i += 2;
                    break;
                }
                i--;
            }
        } else
        {
            i = mEnd - 1;
            while (i > 0)
            {
                b0 = mBuf.get(i);
                if (b0 == 0x0a && i != mEnd - 1)
                {
                    i++;
                    break;
                }
                i--;
            }
        }
        if (i < 0)
            i = 0;
        int mParaSize = mEnd - i;
        int j;
        byte[] buf = new byte[mParaSize];
        for (j = 0; j < mParaSize; j++)
        {
            buf[j] = mBuf.get(i + j);
        }
        return buf;
    }

    protected byte[] readNextParagraph(int mFromPos)
    {
        int mStart = mFromPos;
        int i = mStart;
        byte b0, b1;
        // 根据编码格式判断换行
        if (mCharType.equals("UTF-16LE"))
        {
            while (i < mBufLen - 1)
            {
                b0 = mBuf.get(i++);
                b1 = mBuf.get(i++);
                if (b0 == 0x0a && b1 == 0x00)
                {
                    break;
                }
            }
        } else if (mCharType.equals("UTF-16BE"))
        {
            while (i < mBufLen - 1)
            {
                b0 = mBuf.get(i++);
                b1 = mBuf.get(i++);
                if (b0 == 0x00 && b1 == 0x0a)
                {
                    break;
                }
            }
        } else
        {
            while (i < mBufLen)
            {
                b0 = mBuf.get(i++);
                if (b0 == 0x0a)
                {
                    break;
                }
            }
        }
        int mParaSize = i - mStart;
        byte[] buf = new byte[mParaSize];
        for (i = 0; i < mParaSize; i++)
        {
            buf[i] = mBuf.get(mFromPos + i);
        }
        return buf;
    }

    protected Vector<String> pageDown()
    {
        String strParagraph = "";
        Vector<String> lines = new Vector<String>();
        while (lines.size() < mLineCount && mBufEnd < mBufLen)
        {
            byte[] paraBuf = readNextParagraph(mBufEnd);
            mBufEnd += paraBuf.length;
            try
            {
                strParagraph = new String(paraBuf, mCharType);
            } catch (UnsupportedEncodingException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            String strReturn = "";
            if (strParagraph.indexOf("\r\n") != -1)
            {
                strReturn = "\r\n";
                strParagraph = strParagraph.replaceAll("\r\n", "");
            } else if (strParagraph.indexOf("\n") != -1)
            {
                strReturn = "\n";
                strParagraph = strParagraph.replaceAll("\n", "");
            }

            if (strParagraph.length() == 0)
            {
                lines.add(strParagraph);
            }
            while (strParagraph.length() > 0)
            {
                int mSize = mPaint.breakText(strParagraph, true, mVisibleWidth,
                        null);
                lines.add(strParagraph.substring(0, mSize));
                strParagraph = strParagraph.substring(mSize);
                if (lines.size() >= mLineCount)
                {
                    break;
                }
            }
            if (strParagraph.length() != 0)
            {
                try
                {
                    mBufEnd -= (strParagraph + strReturn).getBytes(mCharType).length;
                } catch (UnsupportedEncodingException e)
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return lines;
    }

    protected void pageUp()
    {
        if (mBufBegin < 0)
            mBufBegin = 0;
        Vector<String> lines = new Vector<String>();
        String strParagraph = "";
        while (lines.size() < mLineCount && mBufBegin > 0)
        {
            Vector<String> paraLines = new Vector<String>();
            byte[] paraBuf = readPreviousParagraph(mBufBegin);
            mBufBegin -= paraBuf.length;
            try
            {
                strParagraph = new String(paraBuf, mCharType);
            } catch (UnsupportedEncodingException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            strParagraph = strParagraph.replaceAll("\r\n", "");
            strParagraph = strParagraph.replaceAll("\n", "");

            if (strParagraph.length() == 0)
            {
                paraLines.add(strParagraph);
            }
            while (strParagraph.length() > 0)
            {
                int nSize = mPaint.breakText(strParagraph, true, mVisibleWidth,
                        null);
                paraLines.add(strParagraph.substring(0, nSize));
                strParagraph = strParagraph.substring(nSize);
            }
            lines.addAll(0, paraLines);
        }
        while (lines.size() > mLineCount)
        {
            try
            {
                mBufBegin += lines.get(0).getBytes(mCharType).length;
                lines.remove(0);
            } catch (UnsupportedEncodingException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        mBufEnd = mBufBegin;
        return;
    }

    protected void prePage() throws IOException
    {
        if (mBufBegin <= 0)
        {
            mBufBegin = 0;
            isFirst = true;
            return;
        } else
            isFirst = false;
        mLines.clear();
        pageUp();
        mLines = pageDown();
    }

    public void nextPage() throws IOException
    {
        if (mBufEnd >= mBufLen)
        {
            isLast = true;
            return;
        } else
            isLast = false;
        mLines.clear();
        mBufBegin = mBufEnd;
        mLines = pageDown();
    }

    public void onDraw(Canvas mCanvas)
    {
        if (mLines.size() == 0)
            mLines = pageDown();
        if (mLines.size() > 0)
        {
            if (mBookBg == null)
                mCanvas.drawColor(mBGColor);
            else
                mCanvas.drawBitmap(mBookBg, 0, 0, null);
            int y = mMarginHeight;
            for (String strLine : mLines)
            {
                y += mFontSize;
                mCanvas.drawText(strLine, mMarginWidth, y, mPaint);
            }
        }
        float fPercent = (float) (mBufBegin * 1.0 / mBufLen);
        DecimalFormat df = new DecimalFormat("#0.0");
        String strPercent = df.format(fPercent * 100) + "%";
        int nPercentWidth = (int) mPaint.measureText("999.9%") + 1;
        mCanvas.drawText(strPercent, mScreenWidth - nPercentWidth,
                mScreenHeight - 5, mPaint);
    }

    public void setBgBitmap(Bitmap mBG)
    {
        mBookBg = mBG;
    }

    public void setBgColor(int mColor)
    {
        mBGColor = mColor;
    }

    public boolean isfirstPage()
    {
        return isFirst;
    }

    public boolean islastPage()
    {
        return isLast;
    }

    public int getBufBegin()
    {
        return mBufBegin;
    }

    public static String get_charset(File file)
    {
        String charset = "GBK";
        byte[] first3Bytes = new byte[3];
        try
        {
            boolean checked = false;
            BufferedInputStream bis = new BufferedInputStream(
                    new FileInputStream(file));
            bis.mark(0);
            int read = bis.read(first3Bytes, 0, 3);
            if (read == -1)
                return charset;
            if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE)
            {
                charset = "UTF-16LE";
                checked = true;
            } else if (first3Bytes[0] == (byte) 0xFE
                    && first3Bytes[1] == (byte) 0xFF)
            {
                charset = "UTF-16BE";
                checked = true;
            } else if (first3Bytes[0] == (byte) 0xEF
                    && first3Bytes[1] == (byte) 0xBB
                    && first3Bytes[2] == (byte) 0xBF)
            {
                charset = "UTF-8";
                checked = true;
            }
            bis.reset();
            if (!checked)
            {
                // int len = 0;
                int loc = 0;

                while ((read = bis.read()) != -1)
                {
                    loc++;
                    if (read >= 0xF0)
                        break;
                    if (0x80 <= read && read <= 0xBF) 
                        break;
                    if (0xC0 <= read && read <= 0xDF)
                    {
                        read = bis.read();
                        if (0x80 <= read && read <= 0xBF)
                            continue;
                        else
                            break;
                    } else if (0xE0 <= read && read <= 0xEF)
                    {// 也有可能出错，但是几率较小
                        read = bis.read();
                        if (0x80 <= read && read <= 0xBF)
                        {
                            read = bis.read();
                            if (0x80 <= read && read <= 0xBF)
                            {
                                charset = "UTF-8";
                                break;
                            } else
                                break;
                        } else
                            break;
                    }
                }
            }

            bis.close();
        } catch (Exception e)
        {
            e.printStackTrace();
        }

        return charset;
    }

    public void setFontColor(int mColor){
        mTextColor = mColor;
        
        mPaint.setColor(mTextColor);
    }
}
